package com.bgi.flutterbluetooth;

import android.annotation.TargetApi;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.Handler;
import android.widget.Toast;

import androidx.annotation.NonNull;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import io.flutter.Log;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry.Registrar;

import java.io.ByteArrayOutputStream;

/**
 * FlutterbluetoothPlugin
 */
public class FlutterbluetoothPlugin implements FlutterPlugin, MethodCallHandler, EventChannel.StreamHandler {
    /// The MethodChannel that will the communication between Flutter and native Android
    ///
    /// This local reference serves to register the plugin with the Flutter Engine and unregister it
    /// when the Flutter Engine is detached from the Activity
    private static final String TAG = "FlutterbluetoothPlugin";

    private Context context;
    private Handler uiHandler;
    private MethodChannel channel;
    private EventChannel eventChannel;
    private EventChannel.EventSink mEventSink;

    private BluetoothAdapter mBluetoothAdapter;
    private Map<String, BluetoothDevice> mapBt;
    private BluetoothDevice mConnectedDevice;
    private BroadcastReceiver mReceiver;

    private BluetoothSocket mBtSocket;

    //ble
    private BluetoothGattCallback mBleCallBack;
    private BluetoothGatt mGatt;
    private String serviceUuid, characteristicUuid, descriptorUuid, alertDialogs;

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
    private void init(Context context) {
        Log.d(TAG, "**********init*************");
        this.context = context;
        uiHandler = new Handler();
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        mapBt = new HashMap<>();
        mReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                Log.d(TAG, "广播接受action：" + action);
                switch (action) {
                    case BluetoothDevice.ACTION_FOUND:
                        pushDevice(intent);
                        break;
                    case BluetoothAdapter.ACTION_DISCOVERY_STARTED:
                        pushScanState(BluetoothMsg.SCAN_STATE_START);
                        break;
                    case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
                        pushScanState(BluetoothMsg.SCAN_STATE_OVER);
                        break;
                    case BluetoothAdapter.ACTION_STATE_CHANGED://蓝牙状态改变为：10 关闭 11 正在打开 12 打开 13 正在关闭
                        pushBlueToothState(intent);
                        break;
                }
            }
        };
        mBleCallBack = new BluetoothGattCallback() {
            @Override
            public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
                super.onConnectionStateChange(gatt, status, newState);
                if (newState == BluetoothGatt.STATE_CONNECTED)
                    gatt.discoverServices();
                else if (newState == BluetoothGatt.STATE_DISCONNECTED)
                    pushConnectedState(BluetoothMsg.CONNECT_STATE_UNCONNECT);
            }

            @Override
            public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
                super.onCharacteristicWrite(gatt, characteristic, status);
            }

            @Override
            public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
                super.onDescriptorWrite(gatt, descriptor, status);
            }

            @Override
            public void onServicesDiscovered(BluetoothGatt gatt, int status) {
                super.onServicesDiscovered(gatt, status);
                boolean flag1 = false;
                boolean flag2 = false;
                boolean flag3 = false;
                BluetoothGattService service = gatt.getService(UUID.fromString(serviceUuid));
                if (service != null) {
                    BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(characteristicUuid));
                    if (characteristic != null) {
                        flag1 = gatt.setCharacteristicNotification(characteristic, true);
                        BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(descriptorUuid));
                        if (descriptor != null) {
                            flag2 = descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                            flag3 = gatt.writeDescriptor(descriptor);
                        }
                    }
                }
                if (flag1 && flag2 && flag3) {
                    mConnectedDevice = gatt.getDevice();
                    pushConnectedState(BluetoothMsg.CONNECT_STATE_CONNECTED);
                } else {
                    pushConnectedState(BluetoothMsg.CONNECT_STATE_FAIL);
                    disconnectBle();
                }
            }

            @Override
            public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
                super.onCharacteristicChanged(gatt, characteristic);
                byte[] values = characteristic.getValue();
                String str = new String(values);
                Log.d(TAG, "BLE蓝牙获取数据：" + str);
                pushReadMsg(str);
            }

            @Override
            public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
                super.onCharacteristicRead(gatt, characteristic, status);
            }
        };
    }

    @Override
    public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
        Log.d(TAG, "************插件注册***********onAttachedToEngine");
        init(flutterPluginBinding.getApplicationContext());
        channel = new MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "plugin.bgi.flutterbluetooth");
        channel.setMethodCallHandler(this);
        eventChannel = new EventChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "plugin.bgi.flutterbluetooth_scan_bluetooth");
        eventChannel.setStreamHandler(this);
    }


    // This static function is optional and equivalent to onAttachedToEngine. It supports the old
    // pre-Flutter-1.12 Android projects. You are encouraged to continue supporting
    // plugin registration via this function while apps migrate to use the new Android APIs
    // post-flutter-1.12 via https://flutter.dev/go/android-project-migration.
    //
    // It is encouraged to share logic between onAttachedToEngine and registerWith to keep
    // them functionally equivalent. Only one of onAttachedToEngine or registerWith will be called
    // depending on the user's project. onAttachedToEngine or registerWith must both be defined
    // in the same class.
    public static void registerWith(Registrar registrar) {
        Log.d(TAG, "************插件注册***********registerWith");
        final MethodChannel channel = new MethodChannel(registrar.messenger(), "plugin.bgi.flutterbluetooth");
        final EventChannel eventChannel = new EventChannel(registrar.messenger(), "plugin.bgi.flutterbluetooth_scan_bluetooth");
        FlutterbluetoothPlugin plugin = new FlutterbluetoothPlugin();
        plugin.init(registrar.context());
        channel.setMethodCallHandler(plugin);
        eventChannel.setStreamHandler(plugin);
    }

    @Override
    public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
        switch (call.method) {
            case "blueState":
                result.success(blueState());
                break;
            case "startDiscovery":
                startDiscovery();
                break;
            case "connect":
                connect(((String) call.argument("address")),((String) call.argument("alertDialogs")), ((String) call.argument("uuid")));
                break;
            case "bleConnect":
                bleConnect(((String) call.argument("address")), ((String) call.argument("serviceUuid")), ((String) call.argument("characteristicUuid")), ((String) call.argument("descriptorUuid")));
                break;
            case "openBluetooth":
                openBlueTooth();
                break;
            case "cancelDiscovery":
                cancelDiscovery();
                break;
            case "readyRead":
                readyRead();
                break;
            case "send":
                send(((String) call.argument("msg")));
                break;
            case "disconnectWithPushState":
                disconnectWithPushState();
                break;
            case "disconnectBleWithPushState":
                disconnectBleWithPushState();
                break;
            case "disconnect":
                disconnect();
                break;
            case "disconnectBle":
                disconnectBle();
                break;
            default:
                result.notImplemented();
                break;
        }
    }

    @Override
    public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
        channel.setMethodCallHandler(null);
    }

    @Override
    public void onListen(Object o, EventChannel.EventSink mEventSink) {
        Log.d(TAG, "注册蓝牙监听");
        this.mEventSink = mEventSink;
        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
        context.registerReceiver(mReceiver, filter);
    }

    @Override
    public void onCancel(Object o) {
        Log.d(TAG, "取消监听蓝牙信息");
        context.unregisterReceiver(mReceiver);
        eventChannel.setStreamHandler(null);
    }

    //获取蓝牙状态 -1 设备不支持蓝牙 10 关闭 11 正在打开 12 打开 13 正在关闭
    @TargetApi(Build.VERSION_CODES.ECLAIR)
    private int blueState() {
        if (mBluetoothAdapter == null) return -1;
        return mBluetoothAdapter.getState();
    }

    //开启蓝牙
    @TargetApi(Build.VERSION_CODES.ECLAIR)
    private void openBlueTooth() {
        Log.d(TAG, "打开蓝牙");
        // 没有打开蓝牙
        if (!mBluetoothAdapter.isEnabled()) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            enableBtIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(enableBtIntent);
        }
    }

    //蓝牙扫描
    @TargetApi(Build.VERSION_CODES.ECLAIR)
    private void startDiscovery() {
        Log.d(TAG, "蓝牙扫描");
        mapBt.clear();
        mBluetoothAdapter.startDiscovery();
    }

    //取消扫描
    @TargetApi(Build.VERSION_CODES.ECLAIR)
    private void cancelDiscovery() {
        Log.d(TAG, "取消蓝牙扫描");
        if (mBluetoothAdapter.isDiscovering())
            mBluetoothAdapter.cancelDiscovery();
    }

    // 当 Discovery 发现了一个设备
    @TargetApi(Build.VERSION_CODES.ECLAIR)
    void pushDevice(Intent intent) {
        // 从 Intent 中获取发现的 BluetoothDevice
        Log.d(TAG, "推送蓝牙设备");
        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
        mapBt.put(device.getAddress(), device);
        mEventSink.success(BluetoothMsg.createDeviceJson(device.getName(), device.getAddress()));
    }

    //推送蓝牙扫描状态
    private void pushScanState(int code) {
        Log.d(TAG, "推送蓝牙扫描状态");
        mEventSink.success(BluetoothMsg.createScanStateJson(code));
    }

    //推送蓝牙状态
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
    private void pushBlueToothState(Intent intent) {
        Log.d(TAG, "推送蓝牙状态");
        disconnectBle();
        disconnect();
        int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
        mEventSink.success(BluetoothMsg.createStateJson(state));
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    private void connect(String address, String alertDialogsString, String uuid) {
        Log.d(TAG, "蓝牙连接");
        Log.d(TAG, "address=="+address+"alertDialogsString=="+alertDialogsString+"uuid=="+uuid);
        disconnect();
        alertDialogs = alertDialogsString;
        BluetoothDevice device = mapBt.get(address);
        if (device != null) {
            try {
                if (mBtSocket != null)
                    mBtSocket.close();
                mBtSocket = device.createRfcommSocketToServiceRecord(UUID.fromString(uuid));
                mBtSocket.connect();
                if (mBtSocket.isConnected()) {
                    mConnectedDevice = device;
                    pushConnectedState(BluetoothMsg.CONNECT_STATE_CONNECTED);
                } else {
                    pushConnectedState(BluetoothMsg.CONNECT_STATE_FAIL);
                }
            } catch (IOException e) {
                pushConnectedState(BluetoothMsg.CONNECT_STATE_FAIL);
            }
        } else {
            pushConnectedState(BluetoothMsg.CONNECT_STATE_FAIL);
        }
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
    private void bleConnect(String address, String serviceUuid, String characteristicUuid, String descriptorUuid) {
        Log.d(TAG, "ble蓝牙连接");
        disconnectBle();
        BluetoothDevice device = mapBt.get(address);
        if (device == null) {
            pushConnectedState(BluetoothMsg.CONNECT_STATE_FAIL);
            return;
        }
        this.serviceUuid = serviceUuid;
        this.characteristicUuid = characteristicUuid;
        this.descriptorUuid = descriptorUuid;
        mGatt = device.connectGatt(context, false, mBleCallBack);
    }

    private void disconnectBleWithPushState() {
        disconnectBle();
        pushConnectedState(BluetoothMsg.CONNECT_STATE_UNCONNECT);
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
    private void disconnectBle() {
        Log.d(TAG, "关闭BLE蓝牙连接");
        serviceUuid = null;
        characteristicUuid = null;
        descriptorUuid = null;
        try {
            if (mGatt != null) {
                mGatt.disconnect();
                mGatt.close();
            }
        } catch (Exception e) {
        } finally {
            mGatt = null;
            mConnectedDevice = null;
        }
    }

    //推送连接状态
    private void pushConnectedState(final int connectedState) {
        uiHandler.post(new Runnable() {
            @TargetApi(Build.VERSION_CODES.ECLAIR)
            @Override
            public void run() {
                switch (connectedState) {
                    case BluetoothMsg.CONNECT_STATE_CONNECTED:
                        mEventSink.success(BluetoothMsg.createConnectedStateJson(connectedState, mConnectedDevice.getName(), mConnectedDevice.getAddress()));
                        break;
                    case BluetoothMsg.CONNECT_STATE_UNCONNECT:
                    case BluetoothMsg.CONNECT_STATE_FAIL:
                        mEventSink.success(BluetoothMsg.createConnectedStateJson(connectedState, null, null));
                        break;
                }
            }
        });
    }

    //准备读取数据 0 蓝牙未连接好 1 准备读取数据失败
    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    private void readyRead() {
        if (mBtSocket == null || !mBtSocket.isConnected()) {
            disconnectWithPushState();
            return;
        }
        new Thread() {
            @Override
            public void run() {
                super.run();
                try {
                    if ("SWI-610".equals(alertDialogs)) {
                        //合并之后的字节数组
                        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
                        while (mBtSocket.isConnected()) {
                            outStream.reset();
                            InputStream inputStream = mBtSocket.getInputStream();
                            //将流(inputs)转成字节数组
                            byte[] buff = new byte[1024]; //buff用于存放循环读取的临时数据
                            int rc = inputStream.read(buff);
                            outStream.write(buff, 0, rc);
                            byte[] in_merge = outStream.toByteArray();
                            pushReadMsgSWI(in_merge);
                        }
                    } else {
                        BufferedReader br = new BufferedReader(new InputStreamReader(mBtSocket.getInputStream()));
                        while (mBtSocket.isConnected()) {
                            String msg = br.readLine();
                            pushReadMsg(msg);
                        }
                    }
                } catch (
                        Exception e) {
                    disconnectWithPushState();
                }
            }
        }.

                start();

    }

    //SWI-610推送读取的数据
    private void pushReadMsgSWI(final byte[] msg) {
        uiHandler.post(new Runnable() {
            @Override
            public void run() {
                mEventSink.success(BluetoothMsg.createReadMsgJsonSWI(msg));
            }
        });
    }

    //Rasber推送读取的数据
    private void pushReadMsg(final String msg) {
        uiHandler.post(new Runnable() {
            @Override
            public void run() {
                mEventSink.success(BluetoothMsg.createReadMsgJson(msg));
            }
        });
    }

    //发送数据
    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    private void send(String msg) {
        Log.d(TAG, "发送数据：" + msg);
        if (mBtSocket != null && mBtSocket.isConnected()) {
            try {
                mBtSocket.getOutputStream().write((msg + "\n").getBytes("utf-8"));
                mBtSocket.getOutputStream().flush();
            } catch (Exception e) {
                disconnectWithPushState();
            }
        } else {
            disconnectWithPushState();
        }
    }

    private void disconnectWithPushState() {
        disconnect();
        pushConnectedState(BluetoothMsg.CONNECT_STATE_UNCONNECT);
    }

    private void disconnect() {
        Log.d(TAG, "关闭蓝牙连接");
        try {
            if (mBtSocket != null) {
                mBtSocket.close();
            }
        } catch (Exception e) {
        } finally {
            mConnectedDevice = null;
            mBtSocket = null;
        }
    }
}
